-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Discussion and POC for using a React Context for the Security Rules page #198578
base: main
Are you sure you want to change the base?
Conversation
🤖 Jobs for this PR can be triggered through checkboxes. 🚧
ℹ️ To trigger the CI, please tick the checkbox below 👇
|
if (sortOrder) { | ||
setSortDirection(sortOrder.direction); | ||
onSortChange(sortOrder.direction); | ||
if ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We seem to have exposed an endless state change loop here, therefore the checks
d55f76d
to
ffc8807
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, take my feedback with a grain of salt since I still lack of a lot of context 😛 (pun intended).
Overall, I think extracting state management and UI logic into this hooks helps to simplify RulesContainer
, and reducing all the prop-drilling is a great effort 👍
I'm only concerned with parts of the state that used to be local and are now centralized here, which might cause additional re-renderings but I haven't done any testing. If you've tested it and works fine then ignore this :)
x-pack/plugins/cloud_security_posture/public/pages/rules/rules_context.tsx
Outdated
Show resolved
Hide resolved
x-pack/plugins/cloud_security_posture/public/pages/rules/rules_context.tsx
Show resolved
Hide resolved
x-pack/plugins/cloud_security_posture/public/pages/rules/index.tsx
Outdated
Show resolved
Hide resolved
setRuleNumber( | ||
changedRuleNumbers?.selectedOptionKeys | ||
? changedRuleNumbers?.selectedOptionKeys | ||
: undefined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you checked how frequently these components re-render? Something I'm a little concerned with is the fact that we're replacing local state that only affected a single component, like this one with the rule number, and now we're putting it into context. Wouldn't the context hook, and the rest of siblings re-render as soon as this value is mutated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Glad you mentioned this, the other components do in fact need to re-render. Changing the rule filter needs to update the rules data fetched for the rules_table and rules_counters components.
If you pull the lastest main
branch and turn on highlight update in the DevTools Profiler you will see many updates even outside of the Rules page.
A change like this is not ONLY about performance and re-renders, this is making the logic easier to understand and work with.
@albertoblaz, thanks for the comments!
I have not seen a difference in re-renders, but using a context such as this where the main business logic for the page is encapsulated in the Context shows me where we can act on performance enhancements. One such example, we are fetching ALL the rules for a specific benchmark on every render (AWS, Kubernetes, etc), we can make some changes there by using a useRef to ensure that all the rules are only fetched once. The Rules page is complex in its current state because we have business logic in components that are dependent on the props that are passed to it, additionally, we have the functionality to update the status of the rules in multiple places. When we need to make changes to the rules page, it is hard to follow the logic because it is spread across 4 component files and their inner components. I like to subscribe to the notion of avoiding hasty performance enhancements, until it is a real problem to solve. |
Thanks for putting up the POC!
|
@maxcold you are 100% right. This is a large context, however, this is mostly what the
When you say rerender, are we talking about rendering a virtual DOM or the page? I do not see a difference in page rendering between this proposal and what is currently in use. This change proposal is more about the readability and maintainability of the page(s). When we hastily address performance before there is a performance problem, we are adding more code that JS needs to parse and execute, which may inhibit the performance of the page, the medicine is worse than the bite. ;) What this POC raises are the places where we can make significant impacts whereas it is currently buried with prop drilling. One example is querying ALL the rules so that we can use them to build table filters and muted rule counts . The only data that can change as a result of user action is the muted rules count the other data, section and rule number list, can be |
My main concern is that this example might not be a good one for the prop drill problem. You have almost all the components on the same level with some exceptions like
It looks very clean, but for me it feels somehow off to share the logic and state like that through huge top level context.
I'm talking about components rerendering when smth in the context changes even of these components do not rely on the piece that changes. For example But I also understand that "smth feels off for me" and concerns about potential performance problems are NOT good arguments, so I wouldn't stand in the way of such refactoring if the team sees value in it |
As I said before, I'm not the most suitable person to chime in on this discussion since I don't know the underlying implementation details. @maxcold said:
This is something I'm a little concerned with, though. If a component re-renders because something unrelated to it changed in the context's state, then that sounds like a performance regression and we should avoid it. I honestly haven't tested this myself locally so please @seanrathier correct me if I misunderstood. Also, on the one hand, I agree with @maxcold that, in this particular case, we might not need to move the state from the parent to a context since the component sub-tree is so simple. But there's not really a "right" answer here, it's a matter of preference. On the other hand, I definitely see some value on keeping part of the changes like turning async callbacks into sync ones, or actually fixing the effect to re-run it when all its dependencies change. Would be great if more people contribute to the discussion @elastic/kibana-cloud-security-posture. @seanrathier has had this PR open for 3 weeks and even though this has low-prio, we should make a decision at some point. Whether we ship this or not, it'd be great to provide some feedback and agree as a team on certain patterns/styles :) |
Lol, as often happens I missed NOT where it mattered :)
|
Summary
Code speaks louder than words. 😉
I put together a real-world example using a complex page of what a refactor to using a React Context would look like so that we can discuss and put together an action plan (or no action) to identify and address changes for other pages.
Things I noticed
Context
allowed me to understand easier how the page behaves during state change.rulesPage
,rulesQuery
andpage
attributes. Thepage
attribute exists inrulesPage
andrulesQuery
but seems to be used interchangeably in the components. This is a good example of what a Context could resolve for us. I've fixed this by removing therulesQuery
andrulesPage
objects and passing mostly primitive types and arrays.Things I could have done but thought it was enough for a demo.
Context
test to test state changes and expected behavioursContext
, this will remove some complexity with mocks since we are creating the datauseReducer
pattern to control state changes. However, this would have made this page complexIssues to resolve
useRef
but we might be able to set a prop inuseQuery
.Checklist
Delete any items that do not apply to this PR.